package com.zhiche.lisa.lspm.service.utils.common;


import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: liuanshun
 * @Description: 缓存对象
 * @Date: Create in 14:18 2018/9/21
 */
public class Cache<T extends Object> {

    /**
     * 是否开启清除失效缓存
     */
    private volatile Boolean clearExpireCacheEnable = true;

    /**
     * 数据可复用
     */
    public volatile boolean DateReusable = false;

    /**
     * 数据缓冲区
     */
    private final Map<String, T> CACHE = new ConcurrentHashMap<String, T>();

    /**
     * 缓冲区注入时间
     */
    private final Map<String, Long> CACHE_PUT_TIME = new ConcurrentHashMap<String, Long>();

    /**
     * 每个缓存生效时间1分钟
     */
    public static final long CACHE_HOLD_TIME_1M = 1 * 60 * 1000L;

    /**
     * 每个缓存生效时间10分钟
     */
    public static final long CACHE_HOLD_TIME_10M = 10 * 60 * 1000L;

    /**
     * 每个缓存生效时间1小时
     */
    public static final long CACHE_HOLD_TIME_1H = 1 * 60 * 60 * 1000L;

    /**
     * 每个缓存生效时间12小时
     */
    public static final long CACHE_HOLD_TIME_12H = 12 * 60 * 60 * 1000L;

    /**
     * 每个缓存生效时间1天
     */
    public static final long CACHE_HOLD_TIME_1D = 1 * 24 * 60 * 60 * 1000L;

    /**
     * 初始化
     */
    public Cache() {
        this.initClearTask();
    }

    /**
     * 初始化
     * @param clearExpireCacheEnable 是否启用自动清理
     */
    public Cache(boolean clearExpireCacheEnable) {
        this.clearExpireCacheEnable = clearExpireCacheEnable;
        this.initClearTask();
    }

    /**
     * 初始化
     * @param clearExpireCacheEnable 是否启用自动清理
     * @param clearExpireCacheTime 自动清理间隔
     */
    public Cache(boolean clearExpireCacheEnable,long clearExpireCacheTime) {
        this.clearExpireCacheEnable = clearExpireCacheEnable;
        this.initClearTask(clearExpireCacheTime);
    }

    /**
     * 启动定时清理任务
     */
    private void initClearTask() {
        //启动清除失效缓存数据
        if (clearExpireCacheEnable) {
            new ClearCacheTask().start();
        }
    }

    /**
     * 启动定时清理任务
     * @param clearExpireCacheTime 定时间隔
     */
    private void initClearTask(long clearExpireCacheTime) {
        //启动清除失效缓存数据
        if (clearExpireCacheEnable) {
            new ClearCacheTask(clearExpireCacheTime).start();
        }
    }

    public Boolean getClearExpireCacheEnable() {
        return clearExpireCacheEnable;
    }

    public void setClearExpireCacheEnable(Boolean clearExpireCacheEnable) {
        this.clearExpireCacheEnable = clearExpireCacheEnable;
    }

    public boolean isDateReusable() {
        return DateReusable;
    }

    public void setDateReusable(boolean dateReusable) {
        DateReusable = dateReusable;
    }

    /**
     * 检查缓存对象是否存在，
     * 若不存在，则返回false
     * 若存在，检查其是否已过有效期，如果已经过了则删除该缓存并返回false
     *
     * @param cacheName
     * @return
     */
    public boolean checkCacheName(String cacheName) {
        Long cacheHoldTime = CACHE_PUT_TIME.get(cacheName);
        if (cacheHoldTime == null || cacheHoldTime == 0L) {
            return false;
        }
        if (cacheHoldTime < System.currentTimeMillis()) {
            remove(cacheName);
            return false;
        }
        return true;
    }

    /**
     * 删除所有缓存
     */
    public void removeAll() {
        CACHE_PUT_TIME.clear();
        CACHE.clear();
    }

    /**
     * 删除某个缓存
     *
     * @param cacheName
     */
    public void remove(String cacheName) {
        CACHE_PUT_TIME.remove(cacheName);
        CACHE.remove(cacheName);
    }

    /**
     * 取出一个缓存对象
     *
     * @param cacheName
     * @return
     */
    public T get(String cacheName) {
        if (checkCacheName(cacheName)) {
            T cache = CACHE.get(cacheName);
            if (!DateReusable) {
                remove(cacheName);
            }
            return cache;
        }
        return null;
    }

    /**
     * 存放一个缓存对象，默认保存时间10分钟
     *
     * @param cacheName
     * @param obj
     */
    public void put(String cacheName, T obj) {
        put(cacheName, obj, CACHE_HOLD_TIME_1M);
    }

    /**
     * 存放一个缓存对象
     *
     * @param cacheName
     * @param obj
     * @param holdTime
     */
    public void put(String cacheName, T obj, long holdTime) {
        CACHE_PUT_TIME.put(cacheName, System.currentTimeMillis() + holdTime);
        CACHE.put(cacheName, obj);
    }

    private class ClearCacheTask extends Thread {
        /**
         * 定期清除失效缓存时效
         */
        private volatile long clearExpireCacheTime;

        ClearCacheTask() {
            this.clearExpireCacheTime=CACHE_HOLD_TIME_10M;
            super.setName("clear cache task start ...");
        }

        ClearCacheTask(long clearExpireCacheTime){
            this.clearExpireCacheTime=clearExpireCacheTime;
            super.setName("clear cache task start ...");
        }

        public long getClearExpireCacheTime() {
            return clearExpireCacheTime;
        }

        public void setClearExpireCacheTime(long clearExpireCacheTime) {
            this.clearExpireCacheTime = clearExpireCacheTime;
        }

        @Override
        public void run() {
            while (clearExpireCacheEnable) {
                try {
                    long now = System.currentTimeMillis();
                    int cacheSize = CACHE.size();
                    int putTimeSize = CACHE_PUT_TIME.size();
                    if (cacheSize == putTimeSize) {
                        if (cacheSize != 0) {
                            CACHE_PUT_TIME.forEach((s, aLong) -> {
                                if (aLong < now) {
                                    remove(s);
                                }
                            });
                        }
                    } else {
                        CACHE.forEach((s, t) -> {
                            if (!CACHE_PUT_TIME.containsKey(s))
                                remove(s);
                        });
                        CACHE_PUT_TIME.forEach((s, t) -> {
                            if (!CACHE.containsKey(s))
                                remove(s);
                        });
                    }
                    Thread.sleep(clearExpireCacheTime);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
